<?php

namespace appApi\controllers;


use appApi\components\ApiParameters;
use appApi\components\RequestFormat;
use appApi\components\ResultResponse;
use appApi\components\ResultStatus;
use appApi\dao\AppApi as AppApiDao;
use yii\web\Controller;
use Yii;

class BaseApiController extends Controller
{
    const REQUEST_TYPE = '';
    /**
     * @var ApiParameters 请求的有效参数
     */
    public $parameters;
    /**
     * @var string 签名的加密token
     */
    public $secretToken;
    /**
     * @var int 日记记录标识
     */
    private $logId = 0;
    /**
     * @var ResultResponse 返回结果
     */
    protected $response;

    public function beforeAction($action)
    {
        if (Yii::$app->getRequest()->isPost == false) {
            echo 'app api request method error!';
            return false;
        }
        $this->bindParam();
        $this->response = new ResultResponse();
        $this->response->format = $this->parameters->format;
        if ($this->verify() == false) {
            return false;
        }
        $this->logRequest();
        return true;
    }

    public function afterAction($action, $result)
    {
        $this->logResponse();
        return parent::afterAction($action, $result);
    }

    /**
     * 绑定参数
     */
    protected function bindParam()
    {
        $req = Yii::$app->getRequest();
        $this->parameters = new ApiParameters($req);
    }

    /**
     * 记录访问日志
     */
    protected function logRequest()
    {
        $params = $this->parameters;
        $logObj = AppApiDao::logRequest(
            self::REQUEST_TYPE,
            $params->accessToken,
            $params->userToken,
            $params->timestamp,
            $params->once,
            $params->method,
            $params->toJson()
        );
        if ($logObj) {
            $this->logId = $logObj->id;
        }
    }

    /**
     * 记录返回结果
     */
    protected function logResponse()
    {
        AppApiDao::logResponse(
            $this->logId,
            $this->response->status,
            $this->response->content
        );
    }

    /**
     * 验证数据完整性
     * @return bool
     * @throws \Exception
     */
    protected function verify()
    {
        if ($this->verifyParam() == false) {
            return false;
        }
        if ($this->verifyToken() == false) {
            return false;
        }
        if ($this->verifyRepeatRequest() == false) {
            return false;
        }
        if ($this->verifySign() == false) {
            return false;
        }
        return true;
    }

    /**
     * 验证参数的完整性
     * @return bool
     */
    protected function verifyParam()
    {
        $re = true;
        if (empty($this->parameters->accessToken)) {
            $re = false;
            $req = Yii::$app->getRequest();
            $access_token = $req->post('access_token');
            $msg = '参数accessToken异常';
        }
        if (!($this->parameters->timestamp)) {
            $re = false;
            $msg = '参数timestamp异常';
        }
        if (empty($this->parameters->once)) {
            $re = false;
            $msg = '参数once异常';
        }
        if (empty($this->parameters->sign)) {
            $re = false;
            $msg = '参数sign异常';
        }
        if ($re == false) {
            $this->sendError(ResultStatus::URL_PARAM_CANNOT_EMPTY, $msg, true);
            Yii::error(ResultStatus::URL_PARAM_CANNOT_EMPTY . '请求参数不全, GET: ' . json_encode($_GET) . ', POST: ' . json_encode($_POST));
        }
        return $re;
    }

    /**
     * 验证access_token
     * @throws \Exception
     */
    protected function verifyToken()
    {
        throw new \Exception('null method');
    }

    /**
     * 是否重复提交
     * @return bool
     */
    protected function verifyRepeatRequest()
    {
        $state = AppApiDao::repeatRequest(
            self::REQUEST_TYPE,
            $this->parameters->accessToken,
            $this->parameters->timestamp,
            $this->parameters->once
        );
        if ($state == true) {
            $this->sendError(ResultStatus::REPEAT_REQUEST, '重复提交请求.', true);
            Yii::error(ResultStatus::REPEAT_REQUEST . '重复提交请求, GET: ' . json_encode($_GET) . ', POST: ' . json_encode($_POST));
        }
        return !$state;
    }

    /**
     * 验证签名
     * @return bool
     */
    protected function verifySign()
    {
        $params = $this->parameters->getUrlParams();
        $params['secret_token'] = $this->secretToken;
        $params['data'] = empty($this->parameters->data) ? '' : $this->parameters->data;
        $sign = $this->createSign($params);
        if ($sign != strtoupper($this->parameters->sign)) {
            $this->sendError(ResultStatus::SIGN_ERROR, '签名错误.', true);
            Yii::error(ResultStatus::SIGN_ERROR . '签名错误, GET: ' . json_encode($_GET) . ', POST: ' . json_encode($_POST));
            return false;
        }
        return true;
    }

    /**
     * 返回错误信息
     * @param $status
     * @param null $message
     * @param bool $echo
     * @return $this
     */
    protected function sendError($status, $message = null, $echo = false)
    {
        $format = $this->parameters->format;
        $format = !!$format ? $format : RequestFormat::JSON;
        $this->response->sendError($format, $status, $message, $this->parameters->attach);
        if ($echo) {
            echo $this->response->content;
        } else {
            return $this->response;
        }
    }

    /**
     * 生成签名
     * @param array $params
     * @return string
     */
    protected function createSign($params)
    {
        ksort($params);
        $formatData = [];
        foreach ($params as $k => $v) {
            if (!empty($v) && $k != 'sign' && $k != 'sign_type') {
                $formatData[] = "$k=$v";
            }
        }
        $signStr = implode('&', $formatData);
        unset($k, $v, $formatData);
        return strtoupper(md5($signStr));
    }

    /**
     * 设置处理结果
     * @param array $result
     * @return ResultResponse
     */
    protected function sendResult($result)
    {
        $format = $this->parameters->format;
        $format = !!$format ? $format : RequestFormat::JSON;
        $status = ResultStatus::SUCCESS;
        $error = '';
        $attach = $this->parameters->attach;
        $timestamp = time();
        $resultStr = '';
        switch ($this->parameters->format) {
            case RequestFormat::JSON:
                $resultStr = $this->response->toJson($result);
                break;
            case RequestFormat::XML:
                $resultStr = $this->response->toXml($result);
                break;
            case RequestFormat::JSONP:
                $resultStr = $this->response->toJson($result);
                break;
        }
        $params = [
            'secret_token' => $this->secretToken,
            'status' => $status,
            'result' => $resultStr,
            'timestamp' => $timestamp,
            'error' => $error,
            'attach' => $attach
        ];
        // 生成签名
        $sign = $this->createSign($params);
        return $this->response->sendResult($format, $status, $resultStr, $sign, $error, $attach, $timestamp);
    }

    /**
     * null处理
     * @param $arr
     * @return mixed
     */
    protected function isNullToStr($arr){
        if(is_array($arr)==false){
            return $arr;
        }
        array_walk_recursive($arr, function(&$val, $key){
            if(is_null($val))
            $val = '';
        });
        return $arr;
    }
}